package cn.shop.front.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.shop.front.entity.req.WithdrawQueryReq;
import cn.shop.front.entity.req.WithdrawReq;
import cn.shop.front.service.BackSettingService;
import cn.shop.front.service.FrontUserService;
import cn.shop.front.service.WithdrawService;
import cn.shop.front.util.AliasUtil;
import cn.shop.front.util.CurrentUserUtil;
import cn.shop.transcation.backSetting.entity.BackSetting;
import cn.shop.transcation.frontUserModule.FrontUser;
import cn.shop.transcation.withdraw.entity.Withdraw;
import cn.shop.utils.ApiResult;
import cn.shop.utils.enums.WithdrawChannel;
import cn.shop.utils.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static cn.shop.utils.constant.SystemSettingConstant.*;

/**
 * 提现申请接口
 *
 * @author : 李一涵
 * @date : Created in 2022-2-25 16:46
 * @description :
 */
@Slf4j
@SaCheckLogin
@RestController
@RequestMapping("/api/v1/withdraw")
public class WithdrawController {

    @Resource
    private FrontUserService frontUserService;
    @Resource
    private WithdrawService withdrawService;
    @Resource
    private BackSettingService settingService;

    @Resource
    private RedissonClient redissonClient;


    /**
     * 提现申请
     *
     * @param req 申请信息
     * @return 返回结果
     */
    @PostMapping("")
    public ApiResult<Map<String, Object>> withdraw(@Validated @RequestBody WithdrawReq req, HttpServletRequest request) {
        return processWithdraw(req, request, WithdrawChannel.APP);
    }

    /**
     * pc提现申请
     *
     * @param req 申请信息
     * @return 返回结果
     */
    @PostMapping("/pc")
    public ApiResult<Map<String, Object>> withdrawPc(@Validated @RequestBody WithdrawReq req, HttpServletRequest request) {
        return processWithdraw(req, request, WithdrawChannel.PC);
    }

    private ApiResult<Map<String, Object>> processWithdraw(WithdrawReq req, HttpServletRequest request, WithdrawChannel channel) {
        long userId = CurrentUserUtil.getUserId();
        FrontUser user = frontUserService.getById(userId);
        if (user.getAutoWithdraw()) {
            return ApiResult.fail("您已设置自动提现，请修改成手动提现后再申请提现");
        }
        if (StrUtil.isBlank(user.getTransactionPassword())) {
            return ApiResult.fail("请设置交易密码", null);
        }
        String domain = AliasUtil.getAlias(request);
        Map<String, String> range = withdrawService.withdrawRange();
        String min = range.get(WITHDRAW_MIN), max = range.get(WITHDRAW_MAX);
        if (NumberUtil.isNumber(min) && NumberUtil.isGreater(new BigDecimal(min), req.getAmount())) {
            return ApiResult.fail("提现金额不能低于" + min + "元");
        }
        if (NumberUtil.isNumber(max) && NumberUtil.isLess(new BigDecimal(max), req.getAmount())) {
            return ApiResult.fail("提现金额不能超过" + max + "元");
        }
        LocalDate now = LocalDate.now();
        BackSetting setting = settingService.findByKey(WITHDRAW_DAY_COUNT);
        long times = NumberUtil.parseInt(setting.getValue());
        if (times > 0) {
            long count = withdrawService.lambdaQuery()
                    .eq(Withdraw::getUserId, user.getId())
                    .gt(Withdraw::getCreateTime, now.atTime(0, 0, 0))
                    .lt(Withdraw::getCreateTime, now.atTime(23,59,59))
                    .count();
            if (count >= times) {
                return ApiResult.fail("今日已提现"+count+"次，已达上限");
            }
        }
        RLock lock = redissonClient.getLock("withdraw-" + userId);
        try {
            if (lock.tryLock(0, 60, TimeUnit.SECONDS)) {
                return withdrawService.withdraw(CurrentUserUtil.getUserId(), req.getAmount(), req.getId(), req.getType(), req.getPassword(), domain, channel);
            } else {
                throw new ServiceException("有正在进行的提现，请1分钟后再试");
            }
        } catch (Exception e) {
            log.warn("提现并发异常", e);
            throw new ServiceException("提现失败，请重试");
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 提现记录
     *
     * @param req 查询条件
     * @return 返回结果
     */
    @GetMapping("")
    public ApiResult<Map<String, Object>> list(@Validated WithdrawQueryReq req) {
        Map<String, Object> data = withdrawService.listPage(CurrentUserUtil.getUserId(), req);
        return ApiResult.success(data);
    }

    /**
     * 查看可提现范围
     * @return 返回结果
     */
    @GetMapping("/range")
    public ApiResult<Map<String, String>> range() {
        return ApiResult.success(withdrawService.withdrawRange());
    }
}
